
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys


""" 定位元素参考博客：https://blog.csdn.net/kelanmomo/article/details/82886718#:~:text=expected,ait%E9%85%8D%E5%90%88%E4%BD%BF%E7%94%A8%E3%80%82
    窗口操作参考博客：https://cloud.tencent.com/developer/article/1524123
    键盘操作参考博客：https://blog.csdn.net/shifengboy/article/details/116731850
"""




class UiTest_Api(object):
    def __init__(self,driver=None,time1=10,time2=10):
        if driver is None:
            # 解决您的链接不是私密链接
            options = webdriver.ChromeOptions()
            options.add_argument("--ignore-certificate-errors")
            self.driver = webdriver.Chrome(options=options)
            self.driver.maximize_window()
        else:
            self.driver = driver
        self.time1 = time1
        self.time2 = time2

    """
    1. locator为一个(by, path)元祖，by是Selenium的一个类(from selenium.webdriver.common.by import By)，
        和我们元素定位中使用的方法相同包括:
        By.CLASS_NAME，
        By.ID，
        By.LINK_TEXT，
        By.NAME，
        By.PARTIAL_LINK_TEXT，
        By.TAG_NAME
        By.CSS_SELECTOR，
        By.XPATH，
    2. element为定位到的元素
    """
    def get_name(self):
        pass

    def get_by(self, method):
        """ 获取by """
        by = None
        if method == 'xpath':
            by = By.XPATH
        elif method == 'linktext':
            by = By.LINK_TEXT
        elif method == 'css':
            by = By.CSS_SELECTOR
        return by

    """隐式等待"""
    def implicitly_wait(self, time=10):
        return self.driver.implicitly_wait(time)

    """判断查找元素"""

    def presence_of_element_located(self, locator):
        '''判断一个元素存在于页面DOM树中，存在则返回元素本身，不存在则报错。'''
        # locator = (By.CLASS_NAME, 'test')  # 定位class='test'的元素
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.presence_of_element_located((locator)))

    def presence_of_all_elements_located(self, locator):
        '''判断定位到的所有元素都存在于页面当中，存在则以list形式返回所有元素本身，不存在则报错'''
        # locator = (By.CLASS_NAME, 'test')  # 定位所有class='test'的元素
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.presence_of_all_elements_located((locator)))

    def invisibility_of_element(self, element):
        '''判断某个元素是不是不可访问或者不存在在DOM树中,存在则返回True,不存在则返回False'''
        # element = driver.find_element_by_id('p2')
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.invisibility_of_element(element))

    def invisibility_of_element_located(self, locator):
        '''判断某个元素是不是不可访问或者不存在在DOM树中,存在则返回True,不存在则返回False '''
        # locator = (By.ID, 'button4')  # 一个并不存在的元素
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.invisibility_of_element_located(locator))

    def visibility_of_element_located(self, locator):
        '''  判断特定元素是否存在于DOM树中并且可见，可见意为元素的高和宽都大于0，元素存在返回元素本身，否则返回False
            这个类和presence_of_element_located(locator)有点像，
            但后者只强调元素存在于DOM树中，可见不可见无所谓，
            而前者要求必须高和宽必须都大于0，因此后者在性能上会稍微快一点点
        '''
        # locator = (By.ID, 'p2') #用这个定位
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.visibility_of_element_located(locator))

    def visibility_of_all_elements_located(self, locator):
        '''判断locator定位的所有元素都存在于DOM树中并且可见，可见意为元素高和宽都大于0，存在则以list形式返回元素，否则返回False'''
        # locator = (By.TAG_NAME, 'p')
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.visibility_of_all_elements_located(locator))

    def visibility_of_any_elements_located(self, locator):
        '''判断locator定位的所有元素中，至少有一个存在于DOM树中并且可见，可见意为元素高和宽都大于0，list形式返回存在的元素，一个元素都不存在的情况下返回空list'''
        # locator = (By.TAG_NAME, 'p')
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.visibility_of_any_elements_located(locator))

    def visibility_of(self, element):
        '''判断element定位的所有元素中，至少有一个存在于DOM树中并且可见，可见意为元素高和宽都大于0，list形式返回存在的元素，一个元素都不存在的情况下返回空list'''
        # element = driver.find_element_by_id('p2')
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.visibility_of(element))

    def staleness_of(self, element):
        '''判断某个元素是否不再附加于于DOM树中(不存在)，不再附加的话返回True，依旧存在返回False'''
        # element = driver.find_element_by_id('p1')
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.staleness_of(element))

    """判断查找元素的属性"""

    def element_to_be_clickable(self, locator):
        '''判断某元素是否可访问并且可启用，比如能够点击，若可以则返回元素本身，否则返回False'''
        #locator = (By.ID, 'button2')  # button是一个不可点击的按钮
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.element_to_be_clickable(locator))

    def element_to_be_selected(self,element):
        '''判断某元素是否被选中 参数为element'''
        # element = driver.find_element_by_id('p2')
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.element_to_be_selected(element))

    def element_located_to_be_selected(self,locator):
        ''' 判断某元素是否被选中,参数为locator '''
        # locator = (By.ID, 'checkbox1') #使用ID来定义这个元祖
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.element_located_to_be_selected(locator))

    def element_selection_state_to_be(self, element, is_selected):  #(element, is_selected)
        '''判断某元素的选中状态是否与预期相同，相同则返回True，不同则返回False'''
        # element = driver.find_element_by_id('p2')
        # is_selected = True/False
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.element_selection_state_to_be(element, is_selected))

    def element_located_selection_state_to_be(self, locator, is_selected):
        '''判断某元素是否与预期相同，相同则返回True，不同则返回False'''
        # locator = (By.ID, 'checkbox1') #使用ID来定义这个元祖
        # is_selected = True/False
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.element_located_selection_state_to_be(locator, is_selected))

    def text_to_be_present_in_element(self, locator, text):
        '''判断给定文本是否出现在特定元素中，若存在则返回True，不存在返回False'''
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.text_to_be_present_in_element(locator, text))

    def text_to_be_present_in_element_value(self, locator, text):
        '''判断某文本是否是存在于特定元素的value值中，存在则返回True，不存在则返回False，对于查看没有value值的元素，也会返回False'''
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.text_to_be_present_in_element_value(locator, text))

    """窗口切换"""

    def frame_to_be_available_and_switch_to_it(self, locator):
        '''判断某个frame是否可以切换过去，若可以则切换到该frame，否则返回False '''
        # locator = (By.XPATH, 'html/body/iframe')  # 定义iframe的locator
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.frame_to_be_available_and_switch_to_it(locator))

    def alert_is_present(self):
        '''判断alert是否存在，若存在则切换到alert，若不存在则返回false'''
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.alert_is_present())

    def number_of_windows_to_be(self,num_windows):
        '''判断窗口数量是否是特定数值，是该数值则返回True，否则返回False'''
        # num_windows = 2
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.number_of_windows_to_be(num_windows))

    def new_window_is_opened(self,current_handles):
        '''通过是否增加了句柄数量判断是否打开新窗口，有新窗口则返回True，没有新窗口返回False'''
        # current_handles = driver.window_handles # 获得当前所有句柄数量,调用本接口
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.new_window_is_opened(current_handles))

    """窗口操作"""

    def set_window_size(self, width, hight):
        '''调整窗口大小'''
        self.driver.set_window_size(width, hight)
    def get_window_position(self):
        '''获取取浏览器当前窗口的坐标位置'''
        return self.driver.get_window_position()
    def set_window_position(self, x, y):
        '''设置置浏览器窗口的位置和大小'''
        self.driver.set_window_position(x, y)
    def set_window_rect(self, x, y, width, hight):
        '''设置浏览器窗口的坐标位置'''
        self.driver.set_window_rect(x, y, width, hight)

    def set_maximize_window(self):
        '''窗口最大化'''
        self.driver.maximize_window()
    def set_fullscreen_window(self):
        '''窗口全屏F11'''
        self.driver.fullscreen_window()

    def driver_refresh(self):
        '''刷新'''
        self.driver.refresh()
    def driver_back(self):
        '''后退'''
        self.driver.back()
    def window_forward(self):
        '''前进'''
        self.driver.forward()
    def driver_quit(self):
        '''关闭'''
        self.driver.quit()
    def driver_close(self):
        self.driver.close()


    """判断查找网页title和url"""

    def title_is(self, text):
        '''判断网页title是否是特定文本（英文区分大小写），若完全相同则返回True，否则返回False'''
        # title = 'this is title' # 查看网页title是否是'this is title'
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.title_is(text))

    def title_contains(self, text):
        '''判断网页title是否包含特定文本（英文区分大小写），若包含则返回True，不包含返回False'''
        # title = 'this' # 查看网页title是否包含'this'
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.title_contains(text))

    def url_changes(self,url):
        '''判断网页是否更改了，若更改了则返回True，若没有更改则返回False
            本例中需要切换窗口才能获得新窗口的URL（driver需要你帮助它切换，不会打开新窗口就自动切换）
        '''
        current_window = self.driver.current_window_handle  # 获取当前窗口句柄
        self.driver.find_element_by_id('link').click()  # 点击link迁移到新窗口
        all_windows = self.driver.window_handles  # 获取所有窗口句柄
        for w in all_windows:  # 利用for循环，切换到新窗口
            if w != current_window:
                self.driver.switch_to_window(w)
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.url_changes(url))

    def url_contains(self,text):
        '''判断网址是否包含特定文本（区分大小写），包含则返回True，不包含则返回False'''
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.url_contains(text))

    def url_matches(self,pattern):
        '''判断网址是否符合特定格式，符合则返回True，不符合则返回False'''
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.url_matches(pattern))

    def url_to_be(self,url):
        '''判断网页是否为特定网页（必须完全符合），是则返回True，否则返回False'''
        return WebDriverWait(self.driver, self.time1, self.time2).until(
            EC.url_to_be(url))

    """悬停"""
    def hover_actions(self,element):
        """ ActionChains 鼠标悬停
            首先需要了解ActionChains的执行原理，
            当你调用ActionChains的方法时，不会立即执行，
            而是会将所有的操作按顺序存放在一个队列里，
            当你调用perform()方法时，队列中的时间会依次执行。
        """
        ActionChains(self.driver).move_to_element(element).perform()

    def java_script(self, js):
        """ js 鼠标悬停"""
        # js = "document.getElementsById('password').style.display='block'"
        self.driver.execute_script(js)

    """鼠标动作"""
    # from selenium.webdriver import ActionChains
    def action_mouse(self):
        # ActionChains(self.driver).move_to_element(element).perform()
        """
        click(on_element=None) ——单击鼠标左键
        click_and_hold(on_element=None) ——点击鼠标左键，不松开
        context_click(on_element=None) ——点击鼠标右键
        double_click(on_element=None) ——双击鼠标左键
        drag_and_drop(source, target) ——拖拽到某个元素然后松开
        drag_and_drop_by_offset(source, xoffset, yoffset) ——拖拽到某个坐标然后松开
        key_down(value, element=None) ——按下某个键盘上的键
        key_up(value, element=None) ——松开某个键
        move_by_offset(xoffset, yoffset) ——鼠标从当前位置移动到某个坐标
        move_to_element(to_element) ——鼠标移动到某个元素
        move_to_element_with_offset(to_element, xoffset, yoffset) ——移动到距某个元素（左上角坐标）多少距离的位置
        perform() ——执行链中的所有动作
        release(on_element=None) ——在某个元素位置松开鼠标左键
        send_keys(*keys_to_send) ——发送某个键到当前焦点的元素
        send_keys_to_element(element, *keys_to_send) ——发送某个键到指定元素
        """
        return ActionChains(self.driver)

    """键盘动作"""
    # from selenium.webdriver.common.keys import Keys
    def action_keyboard(self):
        ''' 结合send_key()使用：
            Keys.BACK_SPACE 	# 回退键(BackSpace)
            Keys.TAB	# 制表键(Tab)
            Keys.ENTER		# 回车键(Enter)
            Keys.SHIFT		# 大小写转换键(Shift)
            Keys.CONTROL	# Control键(Ctrl)
            Keys.ALT	# ALT键(Alt)
            Keys.ESCAPE 	# 返回键(Esc)
            Keys.SPACE 		# 空格键(Space)
            Keys.PAGE_UP		# 翻页键上(Page Up)
            Keys.PAGE_DOWN 		# 翻页键下(Page Down)
            Keys.END		# 行尾键(End)
            Keys.HOME		# 行首键(Home)
            Keys.LEFT		# 方向键左(Left)
            Keys.UP		# 方向键上(Up)
            Keys.RIGHT		# 方向键右(Right)
            Keys.DOWN		# 方向键下(Down)
            Keys.INSERT		# 插入键(Insert)
            DELETE		# 删除键(Delete)
            NUMPAD0 ~ NUMPAD9		# 数字键1-9
            Keys.F5		# 刷新键
            F1 ~ F12		# F1 - F12键
            (Keys.CONTROL, 'a')		# 组合键Control+a，全选
            (Keys.CONTROL, 'c')		# 组合键Control+c，复制
            (Keys.CONTROL, 'x')		# 组合键Control+x，剪切
            (Keys.CONTROL, 'v')		# 组合键Control+v，粘贴
        '''
        # driver.find_element_by_css_selector('#kw').send_keys(Keys.CONTROL, 'a')  # 全选
        return Keys




